package com.dhz.common.token;

import com.dhz.common.redis.RedisException;
import com.dhz.common.redis.RedisTemplate;
import com.dhz.common.utils.UuidUtils;
import com.dhz.common.utils.serializer.SerializerTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * token的统一处理模板
 *
 * Created by FY on 2015/8/12.
 */
public class TokenTemplate extends RedisTemplate {
    private static final Logger logger = LoggerFactory.getLogger(TokenTemplate.class);

    private static final String TOKEN_PREFIX = "token:%s:%s";

    /**
     * 生成token值
     * @return String
     */
    private String token() {
        return UuidUtils.compressedBase58Uuid();
    }

    /**
     * 传入region和key，生成对应的 redis key
     * @param region model name
     * @return redis key
     * <pre>
     * 生成的KEY模式 - token:region:key
     * </pre>
     */
    private String createTokenRedisKey(String region, String key) {
        return String.format(TOKEN_PREFIX, region, key);
    }

    /**
     * 获取token数据
     * @param region region,模块名称
     * @param token token值
     * @return TokenObject
     */
    public TokenObject get(String region, String token) {
        return get(region, token, false, false);
    }

    /**
     * 获取token对应的数据
     * @param region region,模块名称
     * @param token token值
     * @param needExpireTime 需要查询token在redis里生育的有效时间
     * @param needValPkToken 需要返回ValPk条件下的token数量
     * @return TokenObject
     */
    public TokenObject get(String region, String token, boolean needExpireTime, boolean needValPkToken) {
        TokenObject object = new TokenObject();
        object.setRegion(region);
        object.setKey(token);

        String tokenKey = createTokenRedisKey(region, token);
        byte[] byteTokenKey = SerializerTools.serializeKey(tokenKey);
        // 查出token对应的value
        byte[] value = get(byteTokenKey);
        object.setValue(SerializerTools.deserializeValue(value));
        // 如果需要查询token剩余有效时间
        if (needExpireTime) {
            object.setExpire(ttl(byteTokenKey).intValue());
        }
        // 如果需要查询valPk下的token集合
        if (needValPkToken) {
            object.setValPkList(lrange(tokenKey, 0, -1));
        }

        return object;
    }

    /**
     * 设置指定KEY的值,同时设定其过期时间，使用setex命令<br/>
     * <b>SETEX说明：</b><a href="http://redisdoc.com/string/setex.html">SETEX</a>
     * region必填；valPk必填；limit默认1；expire默认0
     * <ul>
     *     <li>region model name</li>
     *     <li>key value key</li>
     *     <li>expire 过期时间，单位是秒</li>
     * </ul>
     * @param to {RedisObject}
     */
    public String create(TokenObject to) {
        // region必填；valPk必填；limit必填；expire默认0
        if (to.getRegion() == null || "".equals(to.getRegion())
                || to.getValue() == null || "".equals(to.getValue())
                || to.getValPk() == null || "".equals(to.getValPk())) {
            throw new RedisException("向Redis设值时，Region，Value，ValPk不能为空");
        }
        if (to.getLimit() < 1) to.setLimit(1);
        // 生成token
        to.setKey(token());
        // 生成要保存Redis的redis key
        String key = createTokenRedisKey(to.getRegion(), to.getKey());
        if (to.getExpire() < 1) {
            set(SerializerTools.serializeKey(key), SerializerTools.serializeValue(to.getValue()));
        } else {
            setex(SerializerTools.serializeKey(key), to.getExpire(), SerializerTools.serializeValue(to.getValue()));
        }
        // 将新的token保存到valPk名下
        Long tokenSize = lpush(to.getValPk(), key);
        // 新的TOKEN保存完成后，根据limit限定token存在的数量
        for (long i = tokenSize; i>to.getLimit(); --i) {
            // 从list的右侧弹出多出来的token记录的key
            String oldToken = rpop(to.getValPk());
            // 删除这个token
            del(oldToken);
        }

        // 返回本次token
        return to.getKey();
    }

}
